<?php
// +----------------------------------------------------------------------
// | Schema 模型服务
// +----------------------------------------------------------------------
// | 主要服务于Schema数据处理
// +----------------------------------------------------------------------
// | 错误编码头 223xxx
// +----------------------------------------------------------------------

namespace common\service;

use common\exception\LogicException;
use common\model\ModuleModel;
use common\model\ProjectTemplateModel;
use common\model\SchemaModel;
use common\model\ModuleRelationModel;
use common\model\FieldModel;
use support\ErrorCode;
use support\SingletonTrait;
use think\module\Module;

class SchemaService
{
    // Field Model 对象
    protected $fieldModel;

    // Schema Model 对象
    protected $schemaModel;

    // Module Model 对象
    protected $moduleModel;

    // Module Relation Model 对象
    protected $moduleRelationModel;

    // Module Code
    protected $moduleCode = [];

    // Module Type
    protected $moduleType = [];

    // Module Data
    protected $moduleData = [];

    // 屏蔽has many 显示类型
    protected $hasManyDoesNotDisplay = [
        "entity" => ["entity", "base"]
    ];

    // 屏蔽has one 显示类型
    protected $hasOneDoesNotDisplay = [
        "project" => ["project_disk", "project_template"]
    ];

    use SingletonTrait;

    /**
     * SchemaService constructor.
     */
    public function __construct()
    {
        $this->fieldModel = model(FieldModel::class);
        $this->schemaModel = model(SchemaModel::class);
        $this->moduleModel = model(ModuleModel::class);
        $this->moduleRelationModel = model(ModuleRelationModel::class);
    }

    /**
     * 添加模块
     * @param $param
     * @return array
     * @throws \Exception
     */
    public function addModule($param)
    {
        $resData = $this->moduleModel->addItem($param);
        if (!$resData) {
            // 添加模块失败错误码 001
            throw_strack_exception($this->moduleModel->getError(), ErrorCode::SCHEMA_ADD_MODULE_FAILURE);
        } else {
            // 返回成功数据
            return $resData;
        }
    }

    /**
     * 修改模块
     * @param $param
     * @return array
     */
    public function modifyModule($param)
    {
        $resData = $this->moduleModel->modifyItem($param);
        if (!$resData) {
            // 修改模块失败错误码 002
            throw_strack_exception($this->moduleModel->getError(), ErrorCode::MODULE_MODIFY_FAILURE);
        } else {
            // 返回成功数据
            return $resData;
        }
    }

    /**
     * 删除模块
     * @param $param
     * @return array
     */
    public function deleteModule($param)
    {
        $resData = $this->moduleModel->deleteItem($param);
        if (!$resData) {
            // 删除模块失败错误码 003
            throw_strack_exception($this->moduleModel->getError(), ErrorCode::MODULE_DELETE_FAILURE);
        } else {
            // 返回成功数据
            return $resData;
        }
    }

    /**
     * 新增Schema数据
     * @param $param
     * @return array|bool
     * @throws \Exception
     */
    public function addSchema($param)
    {
        $addData = [
            'name' => $param["name"],
            'code' => $param["code"],
            'type' => $param["type"]
        ];

        $resData = $this->schemaModel->addItem($addData);

        if (!empty($resData)) {
            if (isset($param["copy_id"]) && !empty($param["copy_id"])) {
                // 存在schema_id 则拷贝给当前新增的Schema
                $moduleRelationOptions = [
                    'filter' => ["schema_id" => $param["copy_id"]],
                    'fields' => 'type,src_module_id,dst_module_id,link_id,node_config,schema_id'
                ];
                $moduleRelationData = $this->moduleRelationModel->selectData($moduleRelationOptions);
                $newSchemaId = ($this->schemaModel->_resData)["id"];
                if ($moduleRelationData["total"] > 0) {
                    foreach ($moduleRelationData["rows"] as $moduleRelationItem) {
                        $moduleRelationItem["schema_id"] = $newSchemaId;
                        $this->moduleRelationModel->addItem($moduleRelationItem);
                    }
                }
            }
            // 返回成功数据
            return $resData;
        } else {
            // 添加Schema失败错误码 005
            throw_strack_exception($this->schemaModel->getError(), ErrorCode::SCHEMA_CREATE_FAILURE);
        }
    }

    /**
     * 修改Schema数据
     * @param $param
     * @return array
     */
    public function modifySchema($param)
    {
        $resData = $this->schemaModel->modifyItem($param);
        if (!$resData) {
            // 修改Schema失败错误码 006
            throw_strack_exception($this->schemaModel->getError(), ErrorCode::SCHEMA_MODIFY_FAILURE);
        } else {
            // 返回成功数据
            return $resData;
        }
    }

    /**
     * 删除数据结构，同时删除关联数据结构
     * @param $param
     * @return array
     */
    public function deleteSchema($param)
    {
        $this->schemaModel->startTrans();
        try {
            $schemaResult = $this->schemaModel->deleteItem(["id" => $param["schema_id"]]);
            if (!$schemaResult) {
                throw new LogicException($this->schemaModel->getError(), ErrorCode::SCHEMA_DELETE_FAILURE);
            } else {
                $moduleRelationResult = $this->moduleRelationModel->deleteItem($param);
                if (!$moduleRelationResult) {
                    throw new LogicException($this->moduleRelationModel->getError(), ErrorCode::SCHEMA_MODULE_RELATION_DELETE_FAILURE);
                }
            }
            $this->schemaModel->commit();
            // 返回成功数据
            return [];
        } catch (\Throwable $e) {
            $this->schemaModel->rollback();
            // 删除Schema失败错误码 007
            throw $e;
        }
    }


    /**
     * 获取指定数据结构关联模型
     * @param $schemaId
     * @return array
     */
    public function getSchemaModuleRelationData($schemaId)
    {
        $moduleRelationData = $this->moduleRelationModel->selectData([
            'filter' => ["schema_id" => $schemaId]
        ]);

        $schemaList = [
            "nodes" => [],
            "edges" => [],
            "ports" => [],
            "groups" => [],
        ];

        if ($moduleRelationData["total"] > 0) {
            foreach ($moduleRelationData["rows"] as $moduleRelationItem) {
                $nodeConfig = $moduleRelationItem['node_config'];

                if (array_key_exists('source', $nodeConfig["node_data"])) {
                    array_push($schemaList["nodes"], $nodeConfig["node_data"]['source']);
                }
                if (array_key_exists('target', $nodeConfig["node_data"])) {
                    array_push($schemaList["nodes"], $nodeConfig["node_data"]['target']);
                }
                if (array_key_exists("edges", $nodeConfig) && !empty($nodeConfig['edges'])) {
                    $nodeConfig['edges']['data']['link_id'] = explode(',', $moduleRelationItem["link_id"]);
                    array_push($schemaList["edges"], $nodeConfig['edges']);
                }

                $existNode[] = $nodeConfig["node_data"]['source']['id'];
            }
        }

        return $schemaList;
    }

    /**
     * 获取module字段数据
     * @param array $options
     * @param string $field
     * @return array
     */
    public function getModuleMapData($field = "code", $options = [])
    {
        // 获取module字典信息
        $moduleList = $this->moduleModel->selectData($options);
        $moduleMapData = array_column($moduleList["rows"], null, $field);
        return $moduleMapData;
    }

    /**
     * 获取固定模块列表
     * @return array
     */
    public function getFixedModuleListExcludeExist()
    {
        $tableData = $this->fieldModel->getTables();

        //已经添加的固定模块
        $options = [
            'fields' => 'code',
            'filter' => ["type" => "fixed"]
        ];

        $existFixedModuleData = $this->moduleModel->selectData($options);

        $existFixedModuleLists = [];
        foreach ($existFixedModuleData["rows"] as $item) {
            array_push($existFixedModuleLists, $item["code"]);
        }

        //去掉strack_前缀
        $fixedModuleList = [];
        foreach ($tableData as $tableName) {
            if (!in_array($tableName, $existFixedModuleLists)) {
                array_push($fixedModuleList, [
                    'id' => $tableName,
                    'name' => $tableName,
                ]);
            }
        }

        return $fixedModuleList;
    }

    /**
     * 获取实体模块列表
     * @return mixed
     */
    public function getEntityModuleList()
    {
        // 获取module字典信息
        $entityModuleList = $this->moduleModel->field('id,name,code')->where(['type' => 'entity'])->select();

        return $entityModuleList;
    }

    /**
     * 获取项目模板可配置模块列表
     * @param $templateId
     * @return array
     */
    public function getProjectTemplateModuleList($templateId)
    {
        // 获取当前template使用的schema_id
        $projectTemplateModel = model(ProjectTemplateModel::class);
        $schemaId = $projectTemplateModel->where(["id" => $templateId])->getField("schema_id");

        // 获取Schema Name
        $schemaName = $this->schemaModel->where(["id" => $schemaId])->getField("name");

        // 获取当前结构module_id
        $moduleIdData = $this->moduleRelationModel->field("src_module_id,dst_module_id")->where(["schema_id" => $schemaId])->select();

        // module id去重
        $moduleIds = [];
        foreach ($moduleIdData as $moduleIdItem) {
            if (!in_array($moduleIdItem["src_module_id"], $moduleIds)) {
                array_push($moduleIds, $moduleIdItem["src_module_id"]);
            }
            if (!in_array($moduleIdItem["dst_module_id"], $moduleIds)) {
                array_push($moduleIds, $moduleIdItem["dst_module_id"]);
            }
        }

        // 分为固定字段和entity固定字段
        $templateModuleList = [
            'schema_name' => $schemaName,
            'fixed' => [
                'title' => 'Fixed module',
                'data' => []
            ],
            'entity' => [
                'title' => 'dynamic module',
                'data' => []
            ]
        ];

        // 查找当前Schema里面数据
        $moduleModel = model(ModuleModel::class);
        $schemaModuleData = $moduleModel->where(["id" => ["IN", join(",", $moduleIds)]])->select();

        // 填入动态模块
        foreach ($schemaModuleData as $schemaModuleItem) {
            if ($schemaModuleItem["type"] === "entity") {
                $schemaModuleItem["name"] = L(ucfirst($schemaModuleItem["code"]));
                array_push($templateModuleList["entity"]["data"], $schemaModuleItem);
            }
        }

        // 允许项目配置的固定模块
        $allowFixedModuleList = [
            'project', // 项目概况
            'base', // 任务
            'note', // 动态
            'file', // 文件
            'file_commit', // 提交文件（1.0的版本）
            'onset', //前期数据
            'timelog', // 时间日志
            'media',
            //'entity' // shot asset 等动态模块
        ];

        $fixedModuleData = $moduleModel->where(["type" => "fixed"])->select();

        // 整理模块
        foreach ($fixedModuleData as $fixedModuleItem) {
            if (in_array($fixedModuleItem["code"], $allowFixedModuleList)) {
                $fixedModuleItem["name"] = L(ucfirst($fixedModuleItem["code"]));
                if ($fixedModuleItem["code"] === "project") {
                    // 把项目模块置顶
                    array_unshift($templateModuleList["fixed"]["data"], $fixedModuleItem);
                } else {
                    array_push($templateModuleList["fixed"]["data"], $fixedModuleItem);
                }
            }
        }

        return $templateModuleList;
    }

    /**
     * 保存关联模块设置
     * @param $param
     * @return array
     * @throws \Exception
     * @throws \Throwable
     */
    public function saveModuleRelation($param)
    {
        $this->moduleRelationModel->startTrans();
        try {
            //删除当前结构的数据
            $this->moduleRelationModel->deleteItem($param['param']['filter']);

            //新增管理数据结构
            foreach ($param["data"] as $item) {
                $resData = $this->moduleRelationModel->addItem($item);
                if (!$resData) {
                    throw new LogicException($this->moduleRelationModel->getError(), ErrorCode::SCHEMA_MODULE_RELATION_SAVE_FAILURE);
                }
            }

            $this->moduleRelationModel->commit();

            // 返回成功数据
            return [];
        } catch (\Throwable $e) {
            $this->moduleRelationModel->rollback();
            // 保存关联模块设置失败错误码 008
            throw $e;
        }
    }

    /**
     * 获取一对多不现实模块配置
     * @param $moduleData
     * @return array|mixed
     */
    protected function getModuleHasManyDoesNotDisplay($moduleData)
    {
        $hasManyDoesNotDisplay = [];
        $moduleKey = $moduleData["type"] === "entity" ? $moduleData["type"] : $moduleData["code"];
        if (array_key_exists($moduleKey, $this->hasManyDoesNotDisplay)) {
            $hasManyDoesNotDisplay = $this->hasManyDoesNotDisplay[$moduleKey];
        }
        return $hasManyDoesNotDisplay;
    }

    /**
     * 保存schema关联结构
     * @param $param
     * @return array
     * @throws \Exception
     */
    public function saveSchemaModuleRelation($param)
    {
        $moduleMapData = $this->getModuleMapData();

        $resData = [];
        foreach ($param as $schemaItem) {
            // 保存schema信息
            $schemaParam = [
                "name" => $schemaItem["name"],
                "code" => $schemaItem["code"],
                "type" => $schemaItem["type"],
            ];

            $schemaResData = $this->addSchema($schemaParam);
            $schemaResData['relation_module'] = [];

            // 保存关联结构信息
            foreach ($schemaItem["relation_data"] as $key => $relationData) {
                if (array_key_exists($key, $moduleMapData)) {
                    $srcModuleData = $moduleMapData[$key];
                    $addModuleRelation = $this->addModuleRelation($relationData, $srcModuleData, $moduleMapData, $schemaResData["id"]);
                    $schemaResData['relation_module'][] = $addModuleRelation;
                }
            }

            $resData[] = $schemaResData;
        }

        return $resData;
    }

    /**
     * 获取关联结构保存的json数据格式
     * @param $uuid
     * @param $moduleData
     * @param string $top
     * @param string $left
     * @return array
     */
    protected function generateRelationNodeData($uuid, $moduleData, $top = "46", $left = "343")
    {
        $data = [
            "h" => "80",
            "w" => "120",
            "id" => $uuid,
            "top" => $top,
            "left" => $left,
            "name" => $moduleData["name"],
            "type" => "module",
            "module_id" => $moduleData["id"],
            "module_code" => $moduleData["code"],
            "module_type" => $moduleData["type"]
        ];
        return $data;
    }

    /**
     * 添加关联结构信息
     * @param $relationData
     * @param $srcModuleData
     * @param $moduleMapData
     * @param $schemaId
     * @return array
     * @throws \Exception
     * @throws \Throwable
     */
    protected function addModuleRelation($relationData, $srcModuleData, $moduleMapData, $schemaId)
    {
        $srcUuid = create_uuid();

        // 保存关联结构
        $this->moduleRelationModel->startTrans();

        try {
            foreach ($relationData as $dataKey => $item) {
                $resData = [];
                if (array_key_exists($item["code"], $moduleMapData)) {
                    // 关联Module信息
                    $dstModuleData = $moduleMapData[$item["code"]];

                    // 判断Link Id
                    if ($item['mapping_type'] == 'belong_to') {
                        if ($srcModuleData["type"] == "entity" && $dstModuleData["type"] == "entity") {
                            $linkId = "parent_id";
                        } else {
                            $moduleCode = $dstModuleData["type"] == "entity" ? $dstModuleData["type"] : $dstModuleData["code"];
                            $linkId = $moduleCode . "_id";
                        }
                    } elseif ($item['mapping_type'] == 'has_one') {
                        $sourceCode = $srcModuleData["type"] == "entity" ? $srcModuleData["type"] : $srcModuleData["code"];
                        $linkId = $sourceCode . "_id";
                    } else {
                        $linkId = "id";
                    }

                    $dstUuid = create_uuid();

                    $saveRelationData = [
                        'type' => $item["mapping_type"],
                        'src_module_id' => $srcModuleData["id"],
                        'dst_module_id' => $dstModuleData["id"],
                        'link_id' => $linkId,
                        'schema_id' => $schemaId,
                        'node_config' => [
                            "edges" => [
                                "data" => [
                                    "type" => "connection",
                                    "label" => $item["mapping_type"]
                                ],
                                "source" => $srcUuid,
                                "target" => $dstUuid
                            ],
                            "node_data" => [
                                "source" => $this->generateRelationNodeData($srcUuid, $srcModuleData),
                                "target" => $this->generateRelationNodeData($dstUuid, $dstModuleData)
                            ]
                        ]
                    ];

                    $resData = $this->moduleRelationModel->addItem($saveRelationData);
                    if (!$resData) {
                        throw new LogicException($this->moduleRelationModel->getError(), ErrorCode::SCHEMA_MODULE_RELATION_ADD_FAILURE);
                    }
                }
            }
            $this->moduleRelationModel->commit();
            return $resData;
        } catch (\Throwable $e) {
            $this->moduleRelationModel->rollback();
            //添加关联结构信息失败
            throw $e;
        }
    }

    /**
     * 获取指定模块的关联模块
     * @param $moduleCode
     * @return mixed
     */
    public function getModuleRelationship($moduleCode)
    {
        $class = get_module_model_name(Module::$moduleDictData['module_index_by_code'][$moduleCode]);

        $objectModel = new $class();
        $objectModel->setCurrentModuleCode($moduleCode);
        return $objectModel->parserFilterModuleRelation(true);
    }
}
